/*
 * Copyright 2008 Google Inc.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package com.google.gwt.jas.linker;

import com.google.gwt.core.ext.LinkerContext;
import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.core.ext.UnableToCompleteException;
import com.google.gwt.core.ext.linker.AbstractLinker;
import com.google.gwt.core.ext.linker.ArtifactSet;
import com.google.gwt.core.ext.linker.ConfigurationProperty;
import com.google.gwt.core.ext.linker.EmittedArtifact;
import com.google.gwt.core.ext.linker.LinkerOrder;
import com.google.gwt.core.ext.linker.LinkerOrder.Order;
import com.google.gwt.core.ext.linker.PublicResource;
import com.google.gwt.dev.util.Util;
import com.google.gwt.thirdparty.guava.common.base.Joiner;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

/**
 * A Linker for compiling ActionScript resources via the Flex MXMLC compiler to produce a single .swf output and
 * module.nocache.js script for loading the SWF.
 */
@LinkerOrder(Order.POST)
public class FlexCompilerLinker extends AbstractLinker {

  public String getDescription() {
    return "FlexCompiler";
  }


  final static int TEMP_DIR_ATTEMPTS = 10000;

  public static File createTempDir() {
    File baseDir = new File(System.getProperty("java.io.tmpdir"));
    String baseName = System.currentTimeMillis() + "-";

    for (int counter = 0; counter < TEMP_DIR_ATTEMPTS; counter++) {
      File tempDir = new File(baseDir, baseName + counter);
      if (tempDir.mkdir()) {
        return tempDir;
      }
    }
    throw new IllegalStateException("Failed to create directory within "
        + TEMP_DIR_ATTEMPTS + " attempts (tried "
        + baseName + "0 to " + baseName + (TEMP_DIR_ATTEMPTS - 1) + ')');
  }

  public ArtifactSet link(TreeLogger logger, LinkerContext context,
      ArtifactSet artifacts)
      throws UnableToCompleteException {

    String flexSdkHome = System.getProperty("flexsdk.home", "");
    logger.log(TreeLogger.INFO, "FlexCompiler home is " + flexSdkHome);

    if (!"".equals(flexSdkHome)) {
      flexSdkHome = flexSdkHome + File.separatorChar + "bin" + File.separatorChar;
    }

    
    List<String> flags = new ArrayList<String>();
    String defaultWidth = "800", defaultHeight = "600";
    String defaultFrameRate = "60";
    
    for (ConfigurationProperty prop : context.getConfigurationProperties()) {
      if (prop.getName().startsWith("flex.")) {
        String flag = " -";
        String flagName = prop.getName();
        // strip off flex. and change . to -
        flagName = flagName.substring(5).replace('.', '-');
        boolean isDefault = flagName.startsWith("default");
        flag += flagName + (isDefault ? " " : "=") +prop.getValue();
        // all default flags must be last
        if (isDefault) {
          flags.add(flag);
        } else {
          flags.add(0, flag);
        }
        if (prop.getName().equals("flex.default.size")) {
          String size[] = prop.getValue().split(" ");
          defaultWidth = size[0];
          defaultHeight = size[1];
        }
        else if (prop.getName().equals("flex.default.frame.rate")) {
          defaultFrameRate = prop.getValue();
        }
      }
    }

    String suffix = System.getProperty("os.name").contains("Windows") ? ".exe" : "";
    String mxmlcCmd = flexSdkHome + "mxmlc" + suffix + " " + Joiner.on(" ").join(flags);

    ArtifactSet toReturn = new ArtifactSet(artifacts);
    Set<PublicResource> resources = artifacts.find(PublicResource.class);
    File tempDir = createTempDir();

    for (PublicResource pub : resources) {
      if (pub.getPartialPath().endsWith(".as")) {
        try {
          logger.log(TreeLogger.INFO, "Writing " + pub.getPartialPath());
          File f = new File(tempDir, pub.getPartialPath());
          f.getParentFile().mkdirs();
          FileOutputStream fos = new FileOutputStream(f, false);
          Util.copy(logger, pub.getContents(logger), fos);
        } catch (FileNotFoundException e) {
          e.printStackTrace();
        }
      }
    }

    Set<EmittedArtifact> sources = artifacts.find(EmittedArtifact.class);
    for (EmittedArtifact e : sources) {

      if (e.getPartialPath().endsWith(".as") && !(e instanceof PublicResource)) {

        logger.log(TreeLogger.INFO, "" + e.getPartialPath());
        String asPath = e.getPartialPath();
        int lastSlash = asPath.lastIndexOf('/');
        if (lastSlash != -1) {
          asPath = asPath.substring(lastSlash + 1);
        }
        String swfPath = asPath.substring(0, asPath.length() - 2) + "swf";
        File swfFile = new File(tempDir, swfPath);
        String bootstrapPath =
            asPath.substring(0, asPath.length() - 2) + "nocache.js";
        try {
          File f = new File(tempDir, asPath);
          FileOutputStream fos = new FileOutputStream(f, false);
          Util.copy(logger, e.getContents(logger), fos);
          String absAsPath = new File(tempDir, asPath).getAbsolutePath();
          logger.log(TreeLogger.INFO, "Compiling " + mxmlcCmd + " " + absAsPath);
          Process proc = Runtime.getRuntime().exec(mxmlcCmd + " " + absAsPath);
          // crappy workaround for Process.waitFor() hanging issue
          Util.copy(logger, proc.getErrorStream(), System.err);
          proc.waitFor();
          toReturn.add(
              emitInputStream(logger, new FileInputStream(swfFile),  swfPath));
          toReturn.remove(e);
          String script = Util.readStreamAsString(
              getClass().getClassLoader().getResourceAsStream(
                  "com/google/gwt/jas/linker/swfobject.js"));
          script += "var so = new SWFObject('" + context.getModuleName() + "/" + swfPath
              + "', 'movie', '" + defaultWidth +"', '"+ defaultHeight + "', '" + defaultFrameRate 
              + "', '#ffffff', 'high');\n "
              + "so.addParam('quality', 'high');\n"
              + "so.addParam('wmode', 'opaque');\n " 
              + "so.addParam('allowScriptAccess','always');\n"
              + "so.write('flash_player');\n";
          toReturn.add(emitString(logger, script, bootstrapPath));

          // leave in on disk for now for now for debugging
          //		f.delete();
          //	        new File(swfPath).delete();
        } catch (Exception err) {
          logger.log(TreeLogger.ERROR, err.toString(), err);
        }
      }
    }

    return toReturn;
  }

}
